#ifndef _world_Simulation_hpp_
#define _world_Simulation_hpp_

/** \file Simulation.hpp
 *  \author Adrian Schweizer
 *  \created  $Sa 22 Sep 03:38:22 pm CEST 2007 schwadri@SchwadriLaptop$
 *  \modified $So 28 Okt 05:00:01 pm CET 2007 schwadri@SchwadriLaptop$
 */

#include <list>
#include <vector>
#include <boost/function.hpp>

#include "Common.hpp"
#include "Entity.hpp"

namespace world {

    /** \brief A Single Collision between two entities
     */
    struct Collision
    {
        Collision() {}

        Collision(Entity* e1, Entity* e2, const vector3r& p, const vector3r& normal,
                  const vector3r& tangent1, const vector3r& tangent2)
        :   entity1(e1),
            entity2(e2),
            position(p),
            n(normal),
            t1(tangent1),
            t2(tangent2)
        { }

        Collision(Entity* e1, Entity* e2, const vector3r& p, const vector3r& n);

        //entities involved in collision
        Entity*     entity1;    ///< 1st entity involved in the collision
        Entity*     entity2;    ///< 2nd entity involved in the collision

        vector3r    position;   ///< Position where collision occured in global coordinate frame
        vector3r    n;          ///< Normal direction of collision
        vector3r    t1;         ///< Tangential direction 1
        vector3r    t2;         ///< Tangential direction 2
    };

    /** \brief Physical Simulation of Game Object Movements and Collision Handling*/
    class Simulation
    {
        typedef std::list<Entity*>                                                      entity_container;   ///<type of entity container

    public:
        typedef boost::function<bool(const Collision&)>                                 collision_callback; ///<Type of collision callback function
        typedef entity_container::const_iterator                                        const_iterator;     ///<Const iterator type over entity container
        typedef entity_container::iterator                                              iterator;           ///<Iterator type for entity container

        /** \brief Contains Settings that control the behaviour of the physical simulation.
         *
         *  These Settings can be configured during lobby mode by the game host.
         */
        struct Options
        {
            Options()
            :   constant_drag(0.0),
                linear_drag(0.0),
                quadratic_drag(0.0),
                epsilon(1.0)
            { }

            real    constant_drag;  ///<Constant Drag coefficient
            real    linear_drag;    ///<Linear Drag coefficient
            real    quadratic_drag; ///<Quadratic Drag coefficient
            real    epsilon;        ///<Epsilon value for collision handling

            template<class Archive>
                void serialize(Archive & ar, const unsigned int version)
                {
                    ar & linear_drag;
                    ar & quadratic_drag;
                    ar & epsilon;
                }
        };

        /** \brief Standard dtor
         *
         */
        Simulation(collision_callback c, const Options& o);

        /** \name Accessors*///@{
        real ConstantDrag() const   { return m_constant_drag_coefficient; }
        real LinearDrag() const     { return m_linear_drag_coefficient; }
        real QuadraticDrag() const  { return m_quadratic_drag_coefficient; }
        real Epsilon() const        { return m_epsilon; }
        real T() const              { return m_t; }
        //@}

        /** \name Mutators*///@{
        void Update(real dt);           ///<Advance World State by \p dt seconds and handle collisions

        void ConstantDrag(real d)       { m_constant_drag_coefficient = d; }
        void LinearDrag(real d)         { m_linear_drag_coefficient = d; }
        void QuadraticDrag(real d)      { m_quadratic_drag_coefficient = d; }
        void Epsilon(real e)            { m_epsilon = e; }

        void Add(Entity* e);            ///<Add a new Entity to the Simulation
        void Remove(Entity* e);         ///<Remove a new Entity from the Simulation
        //@}

    private:
        void DoCollision(Collision& c);   ///<Handle a collision of 2 entities
        void DoBoxSphereCollision(Entity* e1, Entity* e2);
        void DoSphereSphereCollision(Entity* e1, Entity* e2);
        friend class boost::serialization::access;

        template<class Archive>
            void serialize(Archive & ar, const unsigned int version)
            {
                ar & m_constant_drag_coefficient;
                ar & m_linear_drag_coefficient;
                ar & m_quadratic_drag_coefficient;
                ar & m_epsilon;
            }

        std::vector<Entity*>    m_remove_list,
                                m_insert_list;                  ///<lists with entities that should be remove or added to the simulateion
        entity_container        m_entities;                     ///<Contains all the movable Entites in the world
        real                    m_t;                            ///<Current Simulation time
        real                    m_constant_drag_coefficient;    ///<Constant Drag coefficient
        real                    m_linear_drag_coefficient;      ///<Linear Drag coefficient
        real                    m_quadratic_drag_coefficient;   ///<Quadratic Drag coefficient
        real                    m_epsilon;                      ///<Epsilon value for collision handling
        collision_callback      m_handle_collision;             ///<Collision Callback routine for game logic
        //FIXME:std::list<Collision>    m_collisions;                   ///<A list of collisions
    };

} // namespace world
#endif // _world_Simulation_hpp_
